﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Clipping.Infrastructure.Models;
using System.Text.RegularExpressions;
using System.Web;

namespace Clipping.Utils.SearchEngines
{
    public class BaiduNews : ISearchEngine
    {


        protected static Regex timespanRegex = new Regex("最近(\\d+)(小时|天|周|月|年)");
        private static DateTime baseDate = DateTime.Parse("1970-1-1 8:00");
        private Encoding _urlEncoding = Encoding.GetEncoding("gb2312");
        private Encoding _contentEncoding = Encoding.UTF8;
        public void Parse(Uri url, string html, out List<News> news, out string nextPageUrl)
        {
            parse(html, out news, out nextPageUrl);
            if (!string.IsNullOrEmpty(nextPageUrl))
                nextPageUrl = constructAbsoluteUrl(url, nextPageUrl).OriginalString;
            if (news != null)
            {
                foreach (var n in news)
                {
                    if (!string.IsNullOrEmpty(n.SameNewsUrl))
                        n.SameNewsUrl = constructAbsoluteUrl(url, n.SameNewsUrl).OriginalString;
                }
            }
        }

        public Uri ConstructUrl(SearchObject searchObj)
        {
            return constructUrl(searchObj);
        }
        public void Search(SearchObject searchObj, IHtmlDownload downloader, out List<News> news, out string nextPageUrl)
        {
            var url = constructUrl(searchObj);
            string html = downloader.GetHtml(url);
            parse(html, out news, out nextPageUrl);
            if (!string.IsNullOrEmpty(nextPageUrl))
                nextPageUrl = constructAbsoluteUrl(url, nextPageUrl).OriginalString;
            if (news != null)
            {
                foreach (var n in news)
                {
                    if (!string.IsNullOrEmpty(n.SameNewsUrl))
                        n.SameNewsUrl = constructAbsoluteUrl(url, n.SameNewsUrl).OriginalString;
                }
            }
        }

        #region 私有方法
        private Uri constructUrl(SearchObject searchObj)
        {
            //Contract.Requires(searchObj != null);

            StringBuilder sb = new StringBuilder(500);
            sb.Append("http://news.baidu.com/ns?cl=2&rn=20");
            sb.AppendFormat("&q1={0}", HttpUtility.UrlEncode(searchObj.Keyword, this._urlEncoding));

            if (searchObj is PeriodSearchObject)
            {
                PeriodSearchObject so = searchObj as PeriodSearchObject;
                DateTime today = DateTime.Now;
                if (so.SearchPeriod == SearchPeriod.全部时间)
                {
                    if (so.SearchType == SearchType.SearchInContent)
                    {
                        sb.Append("&tn=news&ct=0");
                    }
                    else
                    {
                        sb.Append("&tn=newstitle&ct=0");
                    }
                }
                else if (so.SearchPeriod == SearchPeriod.最近1天 || so.SearchPeriod == SearchPeriod.最近1周 || so.SearchPeriod == SearchPeriod.最近1月)
                {
                    if (so.SearchType == SearchType.SearchInContent)
                    {
                        sb.Append("&tn=newsdy");
                    }
                    else
                    {
                        sb.Append("&tn=newstitledy");
                    }

                    sb.AppendFormat("&bt={0}", 0);
                    sb.AppendFormat("&y0={0}", today.Year);
                    sb.AppendFormat("&m0={0}", today.Month);
                    sb.AppendFormat("&d0={0}", today.Day);
                    sb.AppendFormat("&y1={0}", today.Year);
                    sb.AppendFormat("&m1={0}", today.Month);
                    sb.AppendFormat("&d1={0}", today.Day);
                    sb.AppendFormat("&et={0}", 0);
                    sb.AppendFormat("&begin_date={0:yyyy-M-d}", today);
                    sb.AppendFormat("&end_date={0:yyyy-M-d}", today);
                    int hours = getHours(so.SearchPeriod.ToString());
                    sb.AppendFormat("&mt={0}", hours);
                    sb.AppendFormat("&lm={0}", hours);
                    sb.Append("&s=1&ct1=0&ct=0&class=0&q6=");

                }
                else
                {
                    if (so.SearchType == SearchType.SearchInContent)
                    {
                        sb.Append("&tn=newsdy");
                    }
                    else
                    {
                        sb.Append("&tn=newstitledy");
                    }

                    int hours = getHours(so.SearchPeriod.ToString());
                    DateTime startTime = today.AddHours(0 - hours);
                    sb.Append(getSearchTimeQueryString(startTime, today));
                }
            }
            else if (searchObj is TimespanSearchObject)
            {
                TimespanSearchObject so = searchObj as TimespanSearchObject;
                sb.Append(getSearchTimeQueryString(so.StartTime, so.EndTime));
                if (so.SearchType == SearchType.SearchInContent)
                    sb.Append("&tn=newsdy&ct1=0&ct=0&class=0&q6=");
                else
                    sb.Append("&tn=newstitledy&ct=0&class=0&q6=");
            }

            return new Uri(sb.ToString());
        }

        private Uri constructAbsoluteUrl(Uri baseUri, string url)
        {
            return UriHelper.ConstructAbsoluteUrl(baseUri, url);
        }

        private void parse(string html, out List<News> news, out string nextPageUrl)
        {
            news = new List<News>();
            nextPageUrl = null;
            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(html);

            var liNav = doc.CreateNavigator();

            var divContentSearch = liNav.Select("//div[@id='content_left']/ul/li[@class='result']");
            while (divContentSearch.MoveNext())
            {
                News n = new News();

                var li = divContentSearch.Current;

                var link = li.SelectSingleNode("descendant::h3[@class='c-title']/a");
                if (link != null)
                {
                    n.Title = link.Value;
                    n.Title = System.Web.HttpUtility.HtmlDecode(n.Title);
                    n.Url = link.GetAttribute("href", "");
                }

                var mediaAndTime = li.SelectSingleNode("descendant::p[@class='c-author']");
                string mediaAndTimeStr = string.Empty;
                if (mediaAndTime != null)
                {
                    mediaAndTimeStr = mediaAndTime.Value;
                    string mt = mediaAndTime.Value;
                    if (!string.IsNullOrEmpty(mt))
                    {

                        mt = System.Web.HttpUtility.HtmlDecode(mt);
                        mt = mt.Trim();
                        var m = Regex.Match(mt, @"\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}", RegexOptions.None);
                        if (m.Success)
                        {
                            n.PubDate = m.Value;

                            n.Media = mt.Replace(m.Value, "").Trim();
                        }
                    }
                }

                var summary = li.SelectSingleNode("descendant::div[contains(@class,'c-summary')]");
                if (summary != null)
                {
                    var sameNews = summary.Select("descendant::a[@class='c-more_link']");
                    string sameNewsStr = string.Empty;
                    while (sameNews.MoveNext())
                    {
                        Match m = Regex.Match(sameNews.Current.Value, @"(\d+)\s*条相同新闻");
                        if (m.Success)
                        {
                            n.SameNewsUrl = sameNews.Current.GetAttribute("href", "");
                            n.SameNewsCount = int.Parse(m.Groups[1].Value);
                            sameNewsStr = sameNews.Current.Value;

                            break;
                        }
                    }
                    n.Summary = summary.Value;


                    if (!string.IsNullOrEmpty(summary.Value))
                    {
                        if (!string.IsNullOrEmpty(mediaAndTimeStr))
                        {
                            n.Summary = n.Summary.Replace(mediaAndTimeStr, "");
                        }

                        if (!string.IsNullOrEmpty(sameNewsStr))
                        {
                            n.Summary = n.Summary.Replace(sameNewsStr, "");
                            n.Summary = n.Summary.Replace("&nbsp;-&nbsp;百度快照", "");
                        }
                        else
                        {
                            n.Summary = n.Summary.Replace("百度快照", "");
                        }
                    }
                }
                news.Add(n);
            }




            //标题搜索结果的解析
            var divTitleSearch = liNav.Select("//div[@id='content_left']/ul/li[@class='result title']");

            while (divTitleSearch.MoveNext())
            {
                News n = new News();

                var li = divTitleSearch.Current;

                var link = li.SelectSingleNode("descendant::h3[@class='c-title']/a");
                if (link != null)
                {
                    n.Title = link.Value;
                    n.Url = link.GetAttribute("href", "");
                }

                var mediaAndTime = li.SelectSingleNode("child::div[@class='c-title-author']");
                if (mediaAndTime != null)
                {
                    string mt = mediaAndTime.Value;
                    if (!string.IsNullOrEmpty(mt))
                    {
                        mt = System.Web.HttpUtility.HtmlDecode(mt);
                        mt = mt.Trim();
                        int spaceIndex = System.Text.RegularExpressions.Regex.Match(mt, @"\s").Index;
                        if (spaceIndex > 0)
                        {
                            n.Media = mt.Substring(0, spaceIndex);
                        }

                        var m = Regex.Match(mt, @"\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}", RegexOptions.None);
                        if (m.Success)
                        {
                            n.PubDate = m.Value;
                        }

                    }
                }


                var sameNews = li.Select("descendant::a[@class='c-more_link']");
                while (sameNews.MoveNext())
                {
                    if (sameNews.Current.Value.Contains("条相同新闻"))
                    {
                        n.SameNewsUrl = sameNews.Current.GetAttribute("href", "");

                        break;
                    }
                }

                news.Add(n);

            }



            var nextPage = liNav.SelectSingleNode("//p[@id='page']/a[last()]");
            if (nextPage != null && nextPage.Value != null && nextPage.Value.Contains("下一页"))
            {
                nextPageUrl = nextPage.GetAttribute("href", "");
            }
        }

        private string getSearchTimeQueryString(DateTime startTime, DateTime endTime)
        {
            long bt = (long)((startTime - baseDate).TotalSeconds);
            long et = (long)((endTime - baseDate).TotalSeconds) + 86399;
            StringBuilder sb = new StringBuilder(400);
            sb.AppendFormat("&bt={0}", bt);
            sb.AppendFormat("&y0={0}", startTime.Year);
            sb.AppendFormat("&m0={0}", startTime.Month);
            sb.AppendFormat("&d0={0}", startTime.Day);
            sb.AppendFormat("&y1={0}", endTime.Year);
            sb.AppendFormat("&m1={0}", endTime.Month);
            sb.AppendFormat("&d1={0}", endTime.Day);
            sb.AppendFormat("&et={0}", et);
            sb.AppendFormat("&begin_date={0:yyyy-M-d}", startTime);
            sb.AppendFormat("&end_date={0:yyyy-M-d}", endTime);
            return sb.ToString();
        }

        private int getHours(string timeSpan)
        {
            if (timeSpan == null)
            {
                return 24;
            }
            else
            {
                Match m = timespanRegex.Match(timeSpan);

                if (m == null)
                {
                    return 24;
                }
                else
                {
                    switch (m.Groups[2].Value)
                    {
                        case "小时":
                            return int.Parse(m.Groups[1].Value);
                        case "天":
                            return int.Parse(m.Groups[1].Value) * 24;
                        case "周":
                            return int.Parse(m.Groups[1].Value) * 168;
                        case "月":
                            return int.Parse(m.Groups[1].Value) * 720;
                        case "年":
                            return int.Parse(m.Groups[1].Value) * 8640;
                        default:
                            return 24;
                    }
                }
            }
        }
        #endregion

        #region 属性
        public string Name
        {
            get { return "百度新闻搜索"; }
        }

        public Encoding ContentEncoding
        {
            get { return _contentEncoding; }
        }
        #endregion






    }
}
